package com.general.message.push.websocket;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.general.common.exception.ServiceException;
import com.general.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 王首都
 * @date 2024/01/06
 */
@Slf4j
@Component
@ServerEndpoint("/ws/{userKey}")
public class WsSupport {

    /**
     * 用户在线数量
     */
    private static final AtomicInteger onlineCount = new AtomicInteger(0);

    /**
     * 不同客户端对应的ws对象
     */
    private static final ConcurrentHashMap<String, WsSupport> wsMap = new ConcurrentHashMap<>();

    /**
     * 与客户端连接
     */
    private Session session;

    /**
     * 用户标识
     * 普通用户为userId
     * 大屏则另外定义
     */
    private String userKey;

    @OnOpen
    public void onOpen(Session session, @PathParam("userKey") String userKey) {
        this.session = session;
        this.userKey = userKey;
        this.online(userKey);
        wsMap.put(userKey, this);
    }

    @OnClose
    public void onClose() {
        if (wsMap.containsKey(userKey)) {
            this.offline(userKey);
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        if (StringUtils.isNotBlank(message)) {
            JSONObject jsonObject = null;
            try {
                jsonObject = JSON.parseObject(message);
            } catch (JSONException e) {
                throw new ServiceException("不是有效的JSON格式, eg: {\"name\": \"wang\"}");
            }

            jsonObject.put("from", this.userKey);
            Object receiveUser = jsonObject.get("receiveUser");

            // todo parse message

        }
    }

    public static void ping() {
        if (wsMap.size() == 0) {
            return;
        }
        wsMap.values().forEach(session -> session.sendMessage("ping"));
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        throwable.printStackTrace();
        processError(session, throwable);
    }

    public void sendMessage(Object message) {
        try {
            this.session.getBasicRemote().sendText(JSONObject.toJSONString(message));
        } catch (IOException e) {
            processError(session, e);
            offline(userKey);
        }
    }

    public static void sendMessageToAll(Object message) {
        sendMessageTo(message,wsMap.keySet());
    }

    public static void sendMessageTo(Object message, Set<String> userKeys) {
        for (String userKey : userKeys) {
            sendMessageTo(message, userKey);
        }
    }

    public static void sendMessageTo(Object message, String userKey) {
        if (message == null || StringUtils.isBlank(userKey)) {
            log.error("message or userKey is null");
            return;
        }
        Optional.ofNullable(wsMap.get(userKey))
                .ifPresent(session -> session.sendMessage(message));
    }

    private void online(String userKey) {
        onlineCount.incrementAndGet();
        RecordHelper.recordUserKeyIfValid(userKey);
        sendMessage("connect success");
    }

    private void offline(String userKey) {
        wsMap.remove(userKey);
        RecordHelper.removeUserKeyIfValid(userKey);
        onlineCount.decrementAndGet();
    }

    public static int getOnlineCount() {
        return onlineCount.get();
    }

    private void processError(Session session, Throwable throwable) {
        log.error("error happens for userKey {}, cause {}", this.userKey, throwable.getMessage());
    }

}
